클라이언트 구현하기
MCP 서버가 작동하고 있으니 이제 클라이언트 측을 구축할 차례입니다. 클라이언트는 우리 애플리케이션이 MCP 서버와 통신하고 그 기능에 접근할 수 있게 해주는 역할을 합니다.
클라이언트 아키텍처 이해하기
실제 프로젝트 대부분에서는 MCP 클라이언트 또는 MCP 서버 중 하나만 구현합니다. 이 프로젝트에서는 두 가지가 어떻게 함께 동작하는지 보여주기 위해 둘 다 구축합니다.
MCP 클라이언트는 두 가지 주요 구성 요소로 이루어져 있습니다:
- MCP Client - 세션을 더 쉽게 사용할 수 있도록 우리가 직접 만드는 커스텀 클래스
- Client Session - 서버에 대한 실제 연결 (MCP Python SDK의 일부)
클라이언트 세션은 사용이 끝난 후 리소스를 적절히 정리해야 합니다. 그래서 이 정리 작업을 자동으로 처리하기 위해 커스텀 MCP Client 클래스로 감싸는 것입니다.
클라이언트가 애플리케이션에서 차지하는 역할
우리 애플리케이션의 흐름을 기억하시나요? CLI 코드는 MCP 서버와 관련해 두 가지 주요 작업을 수행해야 합니다:
- Claude에 전달할 사용 가능한 도구 목록 가져오기
- Claude가 요청할 때 도구 실행하기
MCP 클라이언트는 애플리케이션 코드에서 사용할 수 있는 간단한 메서드 호출을 통해 이러한 기능을 제공합니다.
핵심 메서드 구현하기
클라이언트에 두 가지 핵심 메서드를 구현해야 합니다: list_tools()와 call_tool()입니다.
도구 목록 조회 메서드
이 메서드는 서버에서 사용 가능한 모든 도구를 가져옵니다:
async def list_tools(self) -> list[types.Tool]:
result = await self.session().list_tools()
return result.tools
간단합니다. 세션(서버와의 연결)에 접근하여 내장 list_tools() 함수를 호출하고 결과에서 도구 목록을 반환합니다.
도구 호출 메서드
이 메서드는 서버에서 특정 도구를 실행합니다:
async def call_tool(
self, tool_name: str, tool_input: dict
) -> types.CallToolResult | None:
return await self.session().call_tool(tool_name, tool_input)
Claude가 제공한 도구 이름과 입력 매개변수를 서버에 전달하고 결과를 반환합니다.
클라이언트 테스트하기
구현을 테스트하려면 클라이언트를 직접 실행할 수 있습니다. 파일에는 MCP 서버에 연결하고 메서드를 호출하는 테스트 코드가 포함되어 있습니다:
async with MCPClient(
command="uv", args=["run", "mcp_server.py"]
) as client:
result = await client.list_tools()
print(result)
이 테스트를 실행하면 앞서 생성한 read_doc_contents와 edit_document 도구를 포함한 도구 정의가 출력되는 것을 확인할 수 있습니다.
전체 흐름 통합하기
이제 클라이언트가 도구를 나열하고 호출할 수 있으므로 전체 흐름을 테스트해 볼 수 있습니다. 메인 애플리케이션을 실행하고 Claude에게 문서에 대해 질문하면:
- 코드가 클라이언트를 사용해 사용 가능한 도구를 가져옵니다
- 이 도구들이 사용자의 질문과 함께 Claude에게 전달됩니다
-
Claude가
read_doc_contents도구를 사용하기로 결정합니다 - 코드가 클라이언트를 사용해 해당 도구를 실행합니다
- 결과가 Claude에게 다시 전달되고, Claude가 사용자에게 응답합니다
예를 들어, "report.pdf 문서의 내용이 무엇인가요?"라고 질문하면 Claude가 문서 읽기 도구를 사용하여 서버에 설정해 둔 20m 응축기 타워 문서에 대한 정보를 반환합니다.
클라이언트는 애플리케이션 로직과 MCP 서버 사이의 다리 역할을 하며, 기반 연결 세부 사항을 신경 쓰지 않고도 서버 기능에 쉽게 접근할 수 있게 해줍니다.
